#ifndef _QUEUE_HPP_
#define _QUEUE_HPP_

#include "random.h"
#include "time_providers.hpp"
#include "definitions.h"
#include "event_list.h"
#include <iostream>
#include <vector>
using namespace std;

class Queue {
public:
  Queue(TP *time_provider, rng01_t &r, string name)
    : time_provider(time_provider), name(name), 
      rng_dests(r),
      n_jobs(0),
      last_event_time(0), n_jobs_max(0),
      n_departures(0), busy_time(0), area(0), svc_time_total(0)
  {}

  string get_name() {
    return name;
  }

  void set_event_list(Event_List *event_list) {
    this->event_list = event_list;
  }

  void add_destination(Queue *d, double r = 1) {
    destinations.push_back(d);
    rates.push_back(r);
  }

  void add_default_destination(Queue *d) {
    double r = 1.0;
    for(unsigned int i=0; i<rates.size(); ++i)
      r -= rates[i];
    add_destination(d, r);
  }

  bool is_empty() {
    return n_jobs == 0;
  }

  unsigned long get_n_jobs() {
    return n_jobs;
  }

  virtual void departure(Job *, double t) = 0;

  virtual void arrival(Job *, double t) = 0;

  friend ostream &operator<<(ostream &os, const Queue &q) {
    os << q.name;
    return os;
  }

  void stats(stats_t *s, double t) {
    update_accumulators(t);
    s->throughput = throughput;
    s->waiting_time = waiting_time;
    s->utilization = busy_time / t;
    s->load = area / t;
    //details
    s->n_jobs = n_jobs;
    s->n_departures = n_departures;
    s->n_jobs_max = n_jobs_max;
    s->busy_time = busy_time;
    s->svc_time = svc_time_total / n_departures;
  }

protected:
  //static
  TP *time_provider;
  string name;
  vector<Queue *> destinations;
  vector<double> rates;
  Event_List *event_list;
  rng01_t &rng_dests;
  //dynamic
  unsigned long n_jobs;
  double last_event_time;
  //accumulators
  unsigned long n_jobs_max, n_departures;
  double busy_time, area;
  double svc_time_total;
  //indexes
  double throughput, waiting_time;

  void schedule_departure(Job *job) {
    //choose the destination
    Queue *dest = NULL;
    if(destinations.size() == 1)
      dest = destinations[0];
    else {
      double r = rng_dests();
      unsigned int i=0, j;
      double acc = 0;
      for(; i<destinations.size(); ++i) {
	dest = destinations[i];
	j = i;
	acc += rates[i];
	if(r < acc)
	  break;
      }
    }
    //get svc time
    double step = time_provider->get_time();
    double occ_time = last_event_time + step;
    svc_time_total += step;
    //schedule
    event_list->schedule(new Event_Notice(MOVE, last_event_time, occ_time, job, this, dest));
    /*
#ifdef LOG
    cerr << "SCHEDULING: [" << occ_time << "] departure of " << job
	 << " from " << *this << " to " << *dest << endl;
#endif
    */
  }

  void update_accumulators(double t) {
    double interval = t - last_event_time;
    if(n_jobs > 0) {
      busy_time += interval;
      area += interval * n_jobs;
    }
  }
};
#endif
